package com.sky.aop;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/*
    自定义切面类：统一为公共字段赋值
 */
@Aspect  //定义切面类注解
@Component   //被ioc管理
@Slf4j  //日志
public class AutoFillAspect {

    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill) ")
    public void autoFillPointCut(){}

    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        log.info("公共字段填充逻辑开始...");
        //所有在com.sky.mapper中的接口里的方法，如果上面有AutoFill注解，都能够拦截到

        //解析到参数中的实体数据，然后将公共字段数据进行设置
        //1.先通过joinPoint获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //2.获得所拦截得方法对象，获取方法上面得注解
        Method method = signature.getMethod();
        //3.判断注解是否有AutoFill注解，如果没有直接返回，如果有，进一步实现自动填充得逻辑
        AutoFill autoFill = method.getAnnotation(AutoFill.class);
        if (autoFill == null){
            return;
        }
        Object[] args = joinPoint.getArgs();
        //如果没有参数，或者参数长度为0
        if (args==null || args.length==0){
            return;
        }
        //4.自动填充，获取参数得实体对象
        Object entity = args[0];
        //5.通过字段反射技术进行设置值
            //判断是新增还是更新
        OperationType type = autoFill.value();

        //公共数据
        LocalDateTime now = LocalDateTime.now();//当前时间
        Long currentId = BaseContext.getCurrentId(); //当前操作得用户ID

        if (type==OperationType.INSERT){
            //字段反射
            //1.获取字节码对象
            Class<?> cls = entity.getClass();
            //2.获取对应得方法
            Method createTimeMethod = cls.getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
            Method updateTimeMethod = cls.getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
            Method createUserMethod = cls.getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
            Method updateUserMethod = cls.getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);

            //反射执行，调用方法
            createTimeMethod.invoke(entity,now);
            updateTimeMethod.invoke(entity,now);
            createUserMethod.invoke(entity,currentId);
            updateUserMethod.invoke(entity,currentId);

        }else if (type==OperationType.UPDATE){
            //更新：更新得时间，更新得用户
            Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
            Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

            setUpdateTime.invoke(entity,now);
            setUpdateUser.invoke(entity,currentId);
        }
    }

}
